
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>

uint32_t crc32_table[256];

void generate_crc32_table(void) {

    // CRC-32 polynomial: 0x04C11DB7
    // x^32 + x^26 + x^23 + x^22 + x^16 + x^12 + x^11 + x^10 + x^8 + x^7 + x^5 + x^4 + x^2 + x + 1
    uint32_t polynomial = 0;
    polynomial |= 1 << 26; // x^26
    polynomial |= 1 << 23; // x^23
    polynomial |= 1 << 22; // x^22
    polynomial |= 1 << 16; // x^16
    polynomial |= 1 << 12; // x^12
    polynomial |= 1 << 11; // x^11
    polynomial |= 1 << 10; // x^10
    polynomial |= 1 << 8; // x^8
    polynomial |= 1 << 7; // x^7
    polynomial |= 1 << 5; // x^5
    polynomial |= 1 << 4; // x^4
    polynomial |= 1 << 2; // x^2
    polynomial |= 1 << 1; // x^1
    polynomial |= 1 << 0; // x^0
    printf("polynomial: 0x%08X\n", polynomial);  // should be 0x04C11DB7

    uint32_t converted = 0;
    for (uint32_t i = 0; i < 32; i++) {
        if (polynomial & (1 << i)) {
            converted |= 1 << (31 - i);
        }
    }
    printf("converted : 0x%08X\n", converted);  // should be 0xEDB88320

    for (uint32_t i = 0; i < 256; i++) {
        uint32_t c = i;
        for (int j = 0; j < 8; j++) {
            if (c & 1) {
                c = converted ^ (c >> 1);
            } else {
                c >>= 1;
            }
        }
        crc32_table[i] = c;
    }

    printf("crc32_table:\n");
    for (int i = 0; i < sizeof(crc32_table) / sizeof(crc32_table[0]); i++) {
        printf("0x%08X, ", crc32_table[i]);
        if (i % 8 == 7) {
            printf("\n");
        }
    }
    printf("\n");
}

uint32_t crc32(const void* data, size_t len) {
    const uint8_t* bytes = (const uint8_t*)data;
    uint32_t crc = 0xFFFFFFFF;

    for (size_t i = 0; i < len; i++) {
        uint8_t index = (crc ^ bytes[i]) & 0xFF;
        crc = (crc >> 8) ^ crc32_table[index];
    }

    return crc ^ 0xFFFFFFFF;
}

uint32_t crc16_table[256];

void generate_crc16_table(void) {
    // CRC-16 polynomial: 0x8005
    // x^16 + x^15 + x^2 + 1
    uint16_t polynomial = 0;
    polynomial |= 1 << 15; 
    polynomial |= 1 << 2;
    polynomial |= 1 << 0;
    printf("polynomial: 0x%04X\n", polynomial);  // should be 0x8005

    uint16_t converted = 0;
    for (uint16_t i = 0; i < 16; i++) {
        if (polynomial & (1 << i)) {
            converted |= 1 << (15 - i);
        }
    }
    printf("converted : 0x%04X\n", converted);  // should be 0xA001

    for (uint16_t i = 0; i < 256; i++) {
        uint16_t c = i;
        for (int j = 0; j < 8; j++) {
            if (c & 1) {
                c = converted ^ (c >> 1);
            } else {
                c >>= 1;
            }
        }
        crc16_table[i] = c;
    }

    printf("crc16_table:\n");
    for (int i = 0; i < sizeof(crc16_table) / sizeof(crc16_table[0]); i++) {
        printf("0x%04X, ", crc16_table[i]);
        if (i % 8 == 7) {
            printf("\n");
        }
    }
    printf("\n");
}

uint16_t crc16(const void* data, size_t len) {
    const uint8_t* bytes = (const uint8_t*)data;
    uint16_t crc = 0xFFFF;
    
    for (size_t i = 0; i < len; i++) {
        uint8_t index = (crc ^ bytes[i]) & 0xFF;
        crc = (crc >> 8) ^ crc16_table[index];
    }
    
    return crc ^ 0xFFFF;
}

int main() {
    generate_crc32_table();
    generate_crc16_table();

    char test_data[] = "Hello, World!";
    size_t length = sizeof(test_data) - 1;

    uint32_t result32 = crc32(test_data, length);
    uint16_t result16 = crc16(test_data, length);
    printf("Data: %s\n", test_data);
    printf("CRC32: 0x%08X\n", result32);
    printf("CRC16: 0x%04X\n", result16);

    return 0;
}
